package com.zhongpengcheng.controller.common;

import cn.hutool.core.io.FileUtil;
import cn.hutool.extra.cglib.CglibUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zhongpengcheng.blog.dao.pojo.db.FileDO;
import com.zhongpengcheng.blog.enums.FileTypeEnum;
import com.zhongpengcheng.blog.enums.ResultCodeEnum;
import com.zhongpengcheng.blog.event.FileDeleteEvent;
import com.zhongpengcheng.blog.event.FileUploadEvent;
import com.zhongpengcheng.blog.event.TimedCacheDataEvent;
import com.zhongpengcheng.blog.pojo.dto.FileDTO;
import com.zhongpengcheng.blog.pojo.po.FilePO;
import com.zhongpengcheng.blog.pojo.ro.FileUploadRO;
import com.zhongpengcheng.blog.service.FileService;
import com.zhongpengcheng.blog.service.OssService;
import com.zhongpengcheng.blog.util.AssertUtil;
import com.zhongpengcheng.blog.util.PageUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.concurrent.TimeUnit;

/**
 * 文件操作接口
 *
 * @author ZhongPengCheng
 * @version 1.0
 * @since 2021-12-04 15:44:00
 */
@Slf4j
@RestController
public class FileController {
    @Autowired
    private OssService ossService;
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    @Autowired
    private FileService fileService;

    /**
     * 上传文件
     *
     * @param multipartFile 文件对象
     * @param filename      文件名
     * @param fileType      文件类型，默认不限制，该选项会影响文件保存路径
     * @return 上传返回结果
     */
    @PostMapping("/file/upload")
    public FileUploadRO upload(@RequestParam("file") MultipartFile multipartFile,
                               @RequestParam("filename") String filename,
                               @RequestParam(value = "fileType", defaultValue = "DEFAULT") FileTypeEnum fileType) {
        log.info("上传文件信息: name={}, originalFilename={}, contentType={}, size={}, extension={}", multipartFile.getName(),
                multipartFile.getOriginalFilename(), multipartFile.getContentType(), multipartFile.getSize(),
                FileUtil.extName(filename));

        String path = ossService.generateUniquePath(filename, fileType);
        FileUploadRO uploadRO = ossService.save(path, multipartFile, fileType);

        eventPublisher.publishEvent(new TimedCacheDataEvent(path, filename, 120, TimeUnit.SECONDS));
        eventPublisher.publishEvent(new FileUploadEvent(filename, uploadRO));

        return uploadRO;
    }

    /**
     * 分页查询文件
     * @param po 查询参数
     * @return 分页结果
     */
    @PostMapping("/files")
    public Page<FileDTO> listFile(@RequestBody FilePO po) {
        LambdaQueryWrapper<FileDO> queryWrapper = Wrappers.<FileDO>lambdaQuery()
                // 文件名模糊查询
                .like(StringUtils.hasText(po.getFileName()), FileDO::getFileName, po.getFileName())
                // 文件扩展名匹配
                .eq(StringUtils.hasText(po.getExtension()), FileDO::getExtension, po.getExtension())
                // 文件ID模糊查询
                .like(StringUtils.hasText(po.getFileId()), FileDO::getFileId, po.getFileId())
                // 文件储存路径模糊查询
                .like(StringUtils.hasText(po.getOssStoragePath()), FileDO::getOssStoragePath, po.getOssStoragePath())
                // 创建时间>=
                .ge(po.getCreateTimeBegin() != null, FileDO::getCreateTime, po.getCreateTimeBegin())
                // 创建时间<=
                .le(po.getCreateTimeEnd() != null, FileDO::getCreateTime, po.getCreateTimeEnd())
                // 更新时间>=
                .ge(po.getUpdateTimeBegin() != null, FileDO::getUpdateTime, po.getUpdateTimeBegin())
                // 更新时间<=
                .le(po.getUpdateTimeEnd() != null, FileDO::getUpdateTime, po.getUpdateTimeEnd());

        log.info("查询参数={}", queryWrapper.getSqlSelect());
        return PageUtil.toDTO(fileService.page(po.page(), queryWrapper), FileDTO::new);
    }

    /**
     * 查询单个文件
     * @param fileId 文件ID
     * @return 文件信息
     */
    @GetMapping("/file/{fileId}")
    public FileDTO getFile(@PathVariable String fileId) {
        FileDO fileDO = fileService.getOne(Wrappers.<FileDO>lambdaQuery().eq(FileDO::getFileId, fileId));
        AssertUtil.notNull(fileDO, ResultCodeEnum.FILE_NOT_EXISTS);

        return CglibUtil.copy(fileDO, FileDTO.class);
    }

    /**
     * 删除单个文件
     * @param fileId 文件ID
     */
    @DeleteMapping("/file/{fileId}")
    public void deleteFile(@PathVariable String fileId) {
        FileDTO file = this.getFile(fileId);
        AssertUtil.notNull(file, ResultCodeEnum.FILE_NOT_EXISTS);
        // 移除文件失败抛出异常
        AssertUtil.isTrue(fileService.remove(Wrappers.<FileDO>lambdaQuery()
                .eq(FileDO::getFileId, fileId).last("limit 1")), ResultCodeEnum.DELETE_FILE_FAILED);
        // 删除成功触发OSS删除事件
        eventPublisher.publishEvent(new FileDeleteEvent(file.getOssStoragePath()));
    }
}
